package com.yinshu.teleshield.tkbase.utils.verify;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.yinshu.teleshield.tkbase.exception.VerifyException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Path;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.groups.Default;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;

/**
 * 验证接口
 *
 * @Author yangjunxiong
 */
public interface Verify {

    /**
     * 选择性验证
     *
     * @param skipNull     是否跳过空字段的验证
     * @param exceptFields 排除验证的字段
     */
    @SuppressWarnings("unchecked")
    default Optional<String> verify(boolean skipNull, String... exceptFields) throws VerifyException {
        String errorMsg = ValidatorUtils.validate(this, skipNull, exceptFields);
        if (StringUtils.isNotBlank(errorMsg)) {
            throw new VerifyException(errorMsg);
        }
        //校验通过
        return Optional.empty();
    }

    default void extraVerify(boolean skipNull) {
    }

    /**
     * 如果没有指定验证的字段，那么验证所有字段
     *
     * @param fields 要验证的字段
     * @return
     */
    @SuppressWarnings("unchecked")
    default Optional<String> verify(String... fields) throws VerifyException {
        String errorMsg = ValidatorUtils.validate(this, fields);
        if (StringUtils.isNotBlank(errorMsg)) {
            throw new VerifyException(errorMsg);
        }
        //校验通过
        return Optional.empty();
    }

    /**
     * 选择性验证,如果验证失败则抛出【VerifyException】
     *
     * @param skipNull     是否跳过空字段的验证
     * @param exceptFields 排除验证的字段
     */
    default void verifyThrow(boolean skipNull, String... exceptFields) throws VerifyException {
        Optional<String> notify = verify(skipNull, exceptFields);
        if (notify.isPresent()) {
            throw new VerifyException(notify.get());
        }
    }

    /**
     * 如果没有指定验证的字段，那么验证所有字段，如果验证失败则抛出【VerifyException】
     *
     * @param fields 要验证的字段
     * @return
     */
    default void verifyThrow(String... fields) throws VerifyException {
        Optional<String> notify = verify(fields);
        if (notify.isPresent()) {
            throw new VerifyException(notify.get());
        }
    }

    /**
     * 如果存在错误，就抛出自定义异常
     *
     * @param exceptionSupplier
     * @param <X>
     * @throws X
     */
    default <X extends Throwable> void verifyThrow(Supplier<? extends X> exceptionSupplier) throws X {
        Optional<String> notify = verify();
        if (notify.isPresent()) {
            X throwable = exceptionSupplier.get();
            ValidatorUtils.setFieldValue(throwable, "detailMessage", notify);
            throw throwable;
        }
    }

    public class ValidatorUtils {
        private final static Validator validator;
//        private static final String    REGEX_MOBILE = "((\\+86|0086)?\\s*)((134[0-8]\\d{7})|(((13([0-3]|[5-9]))|(14[5-9])|15([0-3]|[5-9])|(16(2|[5-7]))|17([0-3]|[5-8])|18[0-9]|19(1|[8-9]))\\d{8})|(14(0|1|4)0\\d{7})|(1740([0-5]|[6-9]|[10-12])\\d{7}))";
//
//        /**
//         * 判断是否是手机号
//         *
//         * @param tel 手机号
//         * @return boolean true:是  false:否
//         */
//        public static boolean isMobile(String tel) {
//            if (StringUtils.isEmpty(tel)) {
//                return false;
//            }
//            return Pattern.matches(REGEX_MOBILE, tel);
//        }

        static {
            validator = Validation.buildDefaultValidatorFactory().getValidator();
        }

        /**
         * 验证实体中的某个字段是否满足约束条件
         *
         * @param entity        被验证的实体
         * @param propertyNames 被验证的实体中的某个字段
         * @return 成功返回null，否则返回错误信息
         */
        public static <T> String validate(T entity, String... propertyNames) {
            Set<ConstraintViolation<T>> validateSet = validator.validate(entity, Default.class);

            if (CollectionUtils.isNotEmpty(validateSet)) {
                for (ConstraintViolation<T> cv : validateSet) {
                    if (ArrayUtils.isEmpty(propertyNames)) {
                        return cv.getMessage();
                    } else if (ArrayUtils.contains(propertyNames, cv.getPropertyPath().toString())) {
                        return cv.getMessage();
                    }
                }
            }
            return null;
        }

        /**
         * 验证实体是否满足约束条件,失败返回错误消息，否则返回空
         *
         * @param entity       被验证的实体
         * @param skipNull     总的设置是否验证为空字段,即忽略个别字段有@notNull的约束。默认false,将验证为null的字段
         * @param exceptFields 排除不验证的字段,默认验证所有的字段
         * @return 成功返回null，否则返回错误信息
         */
        public static <T> String validate(T entity, boolean skipNull, String... exceptFields) {
            Set<ConstraintViolation<T>> validateSet = validator.validate(entity, Default.class);

            if (CollectionUtils.isNotEmpty(validateSet)) {
                for (ConstraintViolation<T> cv : validateSet) {
                    //排除为空的字段
                    if (skipNull && cv.getInvalidValue() == null) {
                        continue;
                    }
                    //排除指定排除的字段
                    List<String> propertyPathList = new ArrayList<>();
                    Path path = cv.getPropertyPath();
                    for (Path.Node node : path) {
                        if (StringUtils.isBlank(node.getName())) {
                            continue;
                        }
                        propertyPathList.add(node.getName());
                    }
                    String propertyPath = StringUtils.join(propertyPathList, ".");
                    if (ArrayUtils.contains(exceptFields, propertyPath)) {
                        continue;
                    }
                    return cv.getMessage();
                }
            }
            return null;
        }

        /**
         * 根据指定的验证器,验证实体是否满足约束条件
         *
         * @param entity    被验证的实体
         * @param validator 自定义验证器
         * @return 成功返回null，否则返回错误信息
         */
        public static <T> String validate(T entity, Validator validator) {
            Set<ConstraintViolation<T>> validateSet = validator.validate(entity, Default.class);
            if (CollectionUtils.isNotEmpty(validateSet)) {
                ConstraintViolation<T> cv = validateSet.iterator().next();
                return cv.getMessage();
            }
            return null;
        }

        public static void setFieldValue(Object obj, String fieldName, Object value) {
            if (null == obj) {
                return;
            }
            Field targetField = getTargetField(obj.getClass(), fieldName);
            if (targetField == null) {
                return;
            }

            try {
                FieldUtils.writeField(targetField, obj, value);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }

        public static Field getTargetField(Class<?> targetClass, String fieldName) {
            Field field;

            try {
                if (targetClass == null) {
                    return null;
                }

                if (Object.class.equals(targetClass)) {
                    return null;
                }

                field = FieldUtils.getDeclaredField(targetClass, fieldName, true);
                if (field == null) {
                    field = getTargetField(targetClass.getSuperclass(), fieldName);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            return field;
        }
    }
}
